import React, { RefObject, KeyboardEvent, ChangeEvent, ReactElement, useEffect, useRef, useState } from "react";
import { Input, InputProps } from "../Input";
import classNames from "classnames";
import { Icon } from "../Icon"
import { useDebounce, useClickOutside } from "../../hooks"


interface DataSource {
    value: string;
}

interface numberProps {
    number: number;
}
//  T & DataSource  交叉数据类型 等于 T + DataSource
export type DataSourceType<T = {}> = T & DataSource & numberProps;


export interface AutoCompleteProps extends Omit<InputProps, 'onSelect'> {
    fetchSuggestion: (str: string) => DataSourceType[] | Promise<DataSourceType[]>;
    onSelect?: (item: DataSourceType) => void;
    renderOption?: (item: DataSourceType) => ReactElement;
}

export const AutoComplete: React.FC<AutoCompleteProps> = (
    {
        fetchSuggestion,
        onSelect,
        renderOption,
        value,
        ...restProps
    }
) => {

    const fixControlValue = (value: any) => {
        if (typeof value == "undefined" || value == null) {
            return ""
        }
        return value
    }
    // 将不受控组件转换为 受控组件
    restProps.defaultValue && delete restProps.defaultValue
    value = fixControlValue(value)

    
    const [inputValue, setInputValue] = useState(value as string);
    const [suggestions, setSuggestions] = useState<DataSourceType[]>([]);
    const [loading, setLoading] = useState(false)
    const [highlightIndex, setHighlightIndex] = useState(-1)
    const triggerSearch = useRef(false)
    const componentRef = useRef<HTMLDivElement>(null)
    const debouncedValue = useDebounce(inputValue, 500)

    useClickOutside(componentRef, ()=>{
        setSuggestions([])
    })

    useEffect(() => {
        if (debouncedValue && triggerSearch.current) {
            const results = fetchSuggestion(debouncedValue);
            if (results instanceof Promise) {
                setLoading(true)
                results.then(data => {
                    setLoading(false)
                    setSuggestions(data);
                })
            } else {
                setSuggestions(results);
            }

        } else {
            setSuggestions([]);
        }
        setHighlightIndex(-1)
    }, [debouncedValue])


    const highlight = (index: number) => {
        if (index < 0) index = 0
        if (index >= suggestions.length) {
            index = suggestions.length - 1
        }
        setHighlightIndex(index)
    }
    const handleKeyDown = (e: KeyboardEvent<HTMLInputElement>) => {
        switch (e.key) {
            case "Enter":
                suggestions[highlightIndex] && handleSelect(suggestions[highlightIndex])
                break
            case "ArrowDown":
                highlight(highlightIndex + 1)
                break
            case "ArrowUp":
                highlight(highlightIndex - 1)
                break
            case "Escape":
                setSuggestions([])
                break
            default:
                break
        }
    }
    const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
        const value = e.target.value.trim();
        setInputValue(value);
        triggerSearch.current = true
    }

    const handleSelect = (item: DataSourceType) => {
        let value = item.value
        setInputValue(value);
    
        triggerSearch.current = false
        setSuggestions([]);
        if (onSelect) {
            onSelect(item);
        }
    }

    const renderTemplate = (item: DataSourceType) => {

        return renderOption ? renderOption(item) : item.value;
    }

    const generateDropdown = () => {

        return (
            <ul>
                {suggestions && suggestions.map((s, index) => {
                    const cnames = classNames("wave-suggestion-item", {
                        "wave-item-highlight" : index == highlightIndex 
                    })
                    return (
                        <li className={cnames} onClick={() => handleSelect(s)} key={index}>{renderTemplate(s)}</li>
                    )
                })}
            </ul>
        );
    }

    return (
        <div className="wave-auto-complete" ref={componentRef}>
            <Input
                onChange={handleChange}
                value={inputValue}
                onKeyDown={handleKeyDown}

                {...restProps}    
            />

            {loading ? <Icon icon="spinner" spin /> : (suggestions.length > 0 && generateDropdown())}
        </div>
    )
}
